package biblioteca.servlet;

import bibliOnline.db.DBConnection;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;

import bibliOnline.util.details.UserDetails;
import bibliOnline.util.CodificaSHA1;
import bibliOnline.db.UserManager;
import bibliOnline.util.ResponseMessage;

import com.google.gson.Gson;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

/**
 * Servlet che implementa la comunicazione col browser nell'applicazione Web
 * bibliOnline.
 * 
 * Le richieste devono essere inviate alla servlet tramite il metodo "POST"
 * 
 * @author Fabio Righi
 */
@WebServlet("/ComunicaClienti")
public class ComunicaClienti extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final boolean JSON_OK = true;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public ComunicaClienti() {
	super();

    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	ResponseMessage testo_risposta;
	PrintWriter pr = response.getWriter();
	Gson gson = new Gson();


	HttpSession sessione_attuale = request.getSession();
	if (sessione_attuale.isNew()) {
	    UserDetails nuovo_utente = new UserDetails();
	    nuovo_utente.setType(UserDetails.USER);

	    sessione_attuale.setAttribute("user_data", nuovo_utente);
	}

	response.setContentType("application/json");
	testo_risposta = gestisciMessaggio(request.getParameterMap(), sessione_attuale);

	if (testo_risposta.getSimple_return()) {
	    pr.write(testo_risposta.getMessage());
	} else {
	    if (JSON_OK) {
		pr.write(gson.toJson(testo_risposta));
	    } else {
		response.setContentType("text/xml");
		try {
		    StringWriter sw = new StringWriter();
		    JAXBContext context = JAXBContext.newInstance(ResponseMessage.class);
		    Marshaller marshaller = context.createMarshaller();
		    //marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

		    marshaller.marshal(testo_risposta, sw);

		    pr.write(sw.toString());
		} catch (JAXBException ex) {
		    Logger.getLogger(ComunicaClienti.class.getName()).log(Level.SEVERE, null, ex);
		}
	    }
	}

	pr.flush();
    }

    /**
     * Smista la richiesta alla funzione dedicata necessaria. 
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @param sessione_attuale sessione associata al client che ha effettuato la richiesta al server
     * 
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList, AtomicReference
     */
    private ResponseMessage gestisciMessaggio(Map<String, String[]> parametri, HttpSession sessione_attuale) {
	ResponseMessage risposta;
	if (parametri == null) {
	    risposta = new ResponseMessage(false, "Errore nella trasmissione dei parametri", false);
	    return risposta;
	}
	Integer n_comando = Integer.valueOf(parametri.get("tipo_comando")[0]);
	if (n_comando == null) {
	    risposta = new ResponseMessage(false, DBConnection.getProperty("MESSAGE_STRANGE_COMMAND"), false);
	    return risposta;
	}

	switch (n_comando) {
	    case 0:
		risposta = crea_utente(parametri, false, sessione_attuale);
		break;
	    case 1:
		risposta = trova_utente(parametri);
		break;
	    case 2:
		risposta = login_utente(parametri, sessione_attuale);
		break;
	    case 3:
		risposta = rimozione_utente(parametri);
		break;
	    case 4:
		risposta = disconnessione_utente(parametri, sessione_attuale);
		break;
	    case 5:
		risposta = crea_utente(parametri, true, sessione_attuale);
		break; 
	    default:
		risposta = new ResponseMessage(false, DBConnection.getProperty("MESSAGE_INVALID_COMMAND"), false);
		System.out.println(DBConnection.getProperty("MESSAGE_INVALID_COMMAND"));
		break;
	}

	return risposta;
    }

    /**
     * Inserisce un un nuovo utente nel database
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @param tipo_utente il tipo di utente da creare. true se bibliotecario, false altrimenti
     * @param sessione_attuale sessione associata al client che ha effettuato la richiesta al server
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList
     */
    private ResponseMessage crea_utente(Map<String, String[]> parametri, boolean tipo_utente, HttpSession sessione_attuale) {
	String password = parametri.get("reg_password")[0];
	String password_codificata = new String();
	boolean privilegi_login = true;
	ResponseMessage risposta = new ResponseMessage(false, "", false);

	UserDetails utente_nuovo = new UserDetails(), utente_login;

	try {
	    password_codificata = CodificaSHA1.SHA1(password);

	    utente_nuovo.checkAndSetUsername(parametri.get("reg_username")[0]);
	    utente_nuovo.checkAndSetPassword(password, password_codificata);
	    utente_nuovo.checkAndSetFirstname(parametri.get("reg_firstname")[0]);
	    utente_nuovo.checkAndSetLastname(parametri.get("reg_lastname")[0]);
	    utente_nuovo.checkAndSetAddress(parametri.get("reg_address")[0]);
	    if (tipo_utente == false) {
		utente_nuovo.checkAndSetEmail(parametri.get("reg_email")[0]);
	    }

	} catch (java.lang.NullPointerException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INVALID_FIELD"));
	    return risposta;
	} catch (NoSuchAlgorithmException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (UnsupportedEncodingException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (Exception ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(ex.getMessage());
	    return risposta;
	}


	if (tipo_utente == true) {
	    utente_login = (UserDetails) sessione_attuale.getAttribute("user_data");

	    if (!utente_login.getType().equals(UserDetails.LIBRARIAN)) {
		privilegi_login = false;
		risposta.setSuccess(false);
		risposta.setMessage(DBConnection.getProperty("MESSAGE_REGISTRATION_NO_PRIVILEGE"));
	    }
	}

	if (privilegi_login) {
	    risposta = UserManager.insert(utente_nuovo, tipo_utente);
	}

	return risposta;
    }

    /**
     * Effettua il login di un utente
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @param sessione_attuale sessione associata al client che ha effettuato la richiesta al server
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList, HttpSession
     */
    private ResponseMessage login_utente(Map<String, String[]> parametri, HttpSession sessione_attuale) {
	ResponseMessage risposta = new ResponseMessage(false, "", false);
	String password;
	if (parametri.get("login_username") == null) {
	    risposta = new ResponseMessage(false, "Username non pervenuto", false);
	    return risposta;
	}
	if (parametri.get("login_password") == null) {
	    risposta = new ResponseMessage(false, "Password non pervenuta", false);
	    return risposta;
	}

	password = parametri.get("login_password")[0];
	String password_codificata = new String();
	UserDetails utente_check = new UserDetails(), utente_login = null;

	try {
	    password_codificata = CodificaSHA1.SHA1(password);
	    utente_check.checkAndSetUsername(parametri.get("login_username")[0]);
	    utente_check.checkAndSetPassword(password, password_codificata);
	} catch (java.lang.NullPointerException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INVALID_FIELD"));
	    return risposta;
	} catch (NoSuchAlgorithmException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (UnsupportedEncodingException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (Exception ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(ex.getMessage());
	    return risposta;
	}

	utente_login = UserManager.login(utente_check);

	sessione_attuale.setAttribute("user_data", utente_login);

	if (utente_login != null) {
	    risposta.setMessage(utente_login.getType().toString());
	    risposta.setUsername(utente_login.getUsername());
	    risposta.setSuccess(true);
	} else {
	    risposta.setSuccess(false);
	}

	return risposta;
    }

    /**
     * Invalida la sessione associata al client che ha effettuato la richiesta
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @param sessione_attuale sessione associata al client che ha effettuato la richiesta al server
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList, HttpSession
     */
    private ResponseMessage disconnessione_utente(Map<String, String[]> parametri, HttpSession sessione_attuale) {
	ResponseMessage risposta = new ResponseMessage();

	sessione_attuale.invalidate();

	risposta.setMessage("disconnesso");
	risposta.setSuccess(true);

	return risposta;
    }

    /**
     * Verifica se un utente (con un dato username) è presente nel database
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList
     */
    private ResponseMessage trova_utente(Map<String, String[]> parametri) {
	ResponseMessage risposta = new ResponseMessage(false, "", true);
	UserDetails utente_check = new UserDetails();
	boolean esiste_utente;

	try {
	    utente_check.checkAndSetUsername(parametri.get("test_username")[0]);

	    if (Integer.valueOf(parametri.get("tipo_utente")[0]) == 1) {
		utente_check.setType(UserDetails.CUSTOMER);
	    } else {
		utente_check.setType(UserDetails.LIBRARIAN);
	    }
	} catch (java.lang.NullPointerException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INVALID_FIELD"));
	    return risposta;
	} catch (Exception ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(ex.getMessage());
	    return risposta;
	}

	esiste_utente = UserManager.exists(utente_check);

	risposta.setMessage(Boolean.toString(esiste_utente));

	return risposta;
    }

    /**
     * Rimuove un utente dal database
     * 
     * @param parametri i parametri che sono stati inviati al server nella richiesta 
     * @return oggetto rappresentante la risposta da inviare al client
     * 
     * @see Map, ArrayList, ResponseMessage
     */
    private ResponseMessage rimozione_utente(Map<String, String[]> parametri) {
	String password = parametri.get("rem_password")[0];
	ResponseMessage risposta = new ResponseMessage();
	String password_codificata = new String(), risposta_s = null;
	UserDetails utente_remove = new UserDetails();

	try {

	    password_codificata = CodificaSHA1.SHA1(password);

	    utente_remove.checkAndSetUsername(parametri.get("rem_username")[0]);
	    utente_remove.checkAndSetPassword(password, password_codificata);
	} catch (java.lang.NullPointerException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INVALID_FIELD"));
	    return risposta;
	} catch (NoSuchAlgorithmException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (UnsupportedEncodingException ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(DBConnection.getProperty("MESSAGE_INTERNAL_ERROR"));
	    return risposta;
	} catch (Exception ex) {
	    risposta.setSuccess(false);
	    risposta.setMessage(ex.getMessage());
	    return risposta;
	}

	risposta = UserManager.remove(utente_remove);

	return risposta;

    }
    
}
